 /*******************************************************************************
  * Copyright (c) 2006, 2007 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
  * http://www.eclipse.org/legal/epl-v10.html
  *
  * Contributors:
  * IBM Corporation - initial API and implementation
  * Brad Reynolds - bug 171616
  *******************************************************************************/

 package org.eclipse.core.internal.databinding.internal.beans;

 import java.beans.PropertyChangeListener ;
 import java.beans.PropertyDescriptor ;
 import java.lang.reflect.Method ;

 import org.eclipse.core.databinding.beans.IBeanObservable;
 import org.eclipse.core.databinding.observable.Diffs;
 import org.eclipse.core.databinding.observable.map.ComputedObservableMap;
 import org.eclipse.core.databinding.observable.set.IObservableSet;
 import org.eclipse.core.databinding.util.Policy;
 import org.eclipse.core.runtime.IStatus;
 import org.eclipse.core.runtime.Status;

 /**
  * @since 1.0
  *
  */
 public class JavaBeanObservableMap extends ComputedObservableMap implements IBeanObservable {

     private PropertyDescriptor propertyDescriptor;
     
     private PropertyChangeListener elementListener = new PropertyChangeListener () {
         public void propertyChange(final java.beans.PropertyChangeEvent event) {
             if (!updating) {
                 fireMapChange(Diffs.createMapDiffSingleChange(
                         event.getSource(), event.getOldValue(), event
                                 .getNewValue()));
             }
         }
     };

     private ListenerSupport listenerSupport;

     private boolean updating = false;

     /**
      * @param domain
      * @param propertyDescriptor
      */
     public JavaBeanObservableMap(IObservableSet domain,
             PropertyDescriptor propertyDescriptor) {
         super(domain);
         
         this.propertyDescriptor = propertyDescriptor;
         this.listenerSupport = new ListenerSupport(elementListener,
                 propertyDescriptor.getName());
         init();
     }

     protected void hookListener(Object domainElement) {
         listenerSupport.hookListener(domainElement);
     }

     protected void unhookListener(Object domainElement) {
         listenerSupport.unhookListener(domainElement);
     }

     protected Object doGet(Object key) {
         try {
             Method readMethod = propertyDescriptor.getReadMethod();
             if (!readMethod.isAccessible()) {
                 readMethod.setAccessible(true);
             }
             return readMethod.invoke(key, new Object [0]);
         } catch (Exception e) {
             Policy.getLog().log(
                     new Status(IStatus.ERROR, Policy.JFACE_DATABINDING,
                             IStatus.ERROR, "cannot get value", e)); //$NON-NLS-1$
 throw new RuntimeException (e);
         }
     }

     protected Object doPut(Object key, Object value) {
         try {
             Object oldValue = get(key);
             propertyDescriptor.getWriteMethod().invoke(key,
                     new Object [] { value });
             keySet().add(key);
             return oldValue;
         } catch (Exception e) {
             Policy.getLog().log(
                     new Status(IStatus.ERROR, Policy.JFACE_DATABINDING,
                             IStatus.ERROR, "cannot set value", e)); //$NON-NLS-1$
 throw new RuntimeException (e);
         }
     }

     /* (non-Javadoc)
      * @see org.eclipse.core.databinding.beans.IBeanObservable#getObserved()
      */
     public Object getObserved() {
         return keySet();
     }

     /* (non-Javadoc)
      * @see org.eclipse.core.databinding.beans.IBeanObservable#getPropertyDescriptor()
      */
     public PropertyDescriptor getPropertyDescriptor() {
         return propertyDescriptor;
     }
 }

